#include "global.h"
#include <Windows.h>
#include <aclapi.h>
#include "NtService.h"

//
// global variable of the service
//
SERVICE_STATUS g_ServiceStatus;
SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
HANDLE g_lobalServiceStatusEvent;

DWORD
CreateNtServiceStatusEvent(
                           )
{
    DWORD Error = ERROR_SUCCESS;

/*    
    SECURITY_ATTRIBUTES sa;
    SECURITY_DESCRIPTOR sd;
    EXPLICIT_ACCESS ea;
    PACL pACL = NULL;
    //DWORD WorldSidSize = SECURITY_MAX_SID_SIZE;
    //PSID WorldSid = NULL;

    //
    // Create the SID for well known "Everyone" group.
    //

    WorldSid = LocalAlloc(LMEM_FIXED, SECURITY_MAX_SID_SIZE);
    if(WorldSid == NULL)
    {
        ERROR_EXIT((DWORD)E_OUTOFMEMORY);
    }

    //if (!::CreateWellKnownSid(WinWorldSid, NULL, WorldSid, &WorldSidSize))
    //{
    //    ERROR_EXIT(GetLastError());
    //}

    // The ACE will allow Everyone SYNCHRONIZE access to the event object.
    ::SecureZeroMemory(&ea, sizeof(ea));
    ea.grfAccessPermissions = SYNCHRONIZE;
    ea.grfAccessMode = SET_ACCESS;
    ea.grfInheritance= NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea.Trustee.ptstrName  = (LPWSTR)WorldSid;

    Error = ::SetEntriesInAcl(1, &ea, NULL, &pACL);
    if (Error != ERROR_SUCCESS)
    {
        ERROR_EXIT(Error);
    }

    if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
    {
        ERROR_EXIT(GetLastError());
    }

    if (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, TRUE))
    {  
        ERROR_EXIT(GetLastError());
    }

    sa.nLength = sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;
*/
    //
    // Create an Event object, this object should be set to signaled when UPM service is started,
    // and to unsignaled when UPM service is not running. Everyone are allowed to sync on it.
    //
    g_lobalServiceStatusEvent = ::CreateEvent(
        //&sa,
        NULL,
        TRUE,
        FALSE,
        NT_SERVICE_SYNC_EVENT_NAME
        );
    
    if (g_lobalServiceStatusEvent == NULL)
    {
        ERROR_EXIT(GetLastError());
    }

exit:
    /*
    if (pACL != NULL)
    {
        ::LocalFree(pACL);
    }

    if (WorldSid != NULL)
    {
        ::LocalFree(WorldSid);
    }*/

    return Error;
}

DWORD
UpdateServiceStatus(
                    __in DWORD CurrentState,
                    __in DWORD ServiceExitCode,
                    __in DWORD WaitHint
                    )

/*++

    Routine Description:

    This function is a wrapper for SetServiceStatus(...)

    Arguments:

        DWORD CurrentState - The Current State.

        DWORD ServiceExitCode - The exit code.

        DWORD WaitHint - This specifies the interval in milliseconds before the 
        service status changes again.

    Return value:

        ERROR_SUCCESS - Success.

--*/

{
    DWORD Error = ERROR_SUCCESS;

    static DWORD CheckPoint = 1;

    // TraceEvents(TRACE_LEVEL_INFORMATION, UPM_INFO,
    //     "UPM: Reporting status to SCM: CurrentState = 0x%x, ServiceExitCode = 0x%x, WaitHint = %u\n",
    //     CurrentState, ServiceExitCode, WaitHint);

    //
    // dwControlsAccepted - This is used to indicate the control codes that will be handled 
    //      by our service handler
    //
    if (CurrentState == SERVICE_START_PENDING)
    {
        g_ServiceStatus.dwControlsAccepted = 0;
    }
    else
    {
        g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    }

    //
    // Specific current state of the service.
    //
    g_ServiceStatus.dwCurrentState = CurrentState;

    //
    // Error code the service uses to report an error that occurs when it is 
    // starting or stopping
    //
    if (ServiceExitCode != 0)
    {
        //
        // Specific the dwServiceSpecificExitCode is valid
        //
        g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
    }
    else
    {
        g_ServiceStatus.dwWin32ExitCode = 0;
    }

    //
    // Service-specific error code that the service returns when an error 
    // occurs while the service is starting or stopping.
    //
    g_ServiceStatus.dwServiceSpecificExitCode = ServiceExitCode;
    
    //
    // Estimated time required for a pending start, stop, pause
    //
    g_ServiceStatus.dwWaitHint = WaitHint;

    if ((CurrentState == SERVICE_RUNNING) ||
        (CurrentState == SERVICE_STOPPED))
    {
        g_ServiceStatus.dwCheckPoint = 0;
    }
    else
    {
        g_ServiceStatus.dwCheckPoint = CheckPoint++;
    }

    //
    // Report the status of the service to the service control manager.
    //
    if (!SetServiceStatus(
            g_ServiceStatusHandle,
            &g_ServiceStatus))
    {
        ERROR_EXIT(GetLastError());
    }

exit:

    return Error;
}

#ifdef __cplusplus
extern "C"
#endif
DWORD WINAPI service_handle(
    __in DWORD dwControl,
    __in DWORD dwEventType,
    __in LPVOID lpEventData,
    __in LPVOID lpContext
)

/*++

    Routine Description:

    This function sets up the nt service and calls SchedulerServiceStart(...).

    Arguments:

        DWORD dwControl - The control code.

        DWORD dwEventType - The event type.

        DWORD lpEventData - The event data.

        DWORD lpContext - The user-defined data.

    Return value:

        The return value for this function depends on the control code received.
        The following list identifies the rules for this return value:


        If your service does not handle the control, return
            ERROR_CALL_NOT_IMPLEMENTED.
        If your service handles SERVICE_CONTROL_DEVICEEVENT, return NO_ERROR to
            grant the request and an error code to deny the request.
        If your service handles SERVICE_CONTROL_HARDWAREPROFILECHANGE, return
            NO_ERROR to grant the request and an error code to deny the request.
        If your service handles SERVICE_CONTROL_POWEREVENT, return NO_ERROR to
            grant the request and an error code to deny the request.
        For all other control codes your service handles, return NO_ERROR.

--*/

{
    UNREFERENCED(dwEventType);
    UNREFERENCED(lpEventData);
    UNREFERENCED(lpContext);
    
    switch (dwControl)
    {
        //
        // Stop the service
        //
        case SERVICE_CONTROL_STOP:
        case SERVICE_CONTROL_SHUTDOWN:
        {
            (VOID)UpdateServiceStatus(
                SERVICE_STOP_PENDING,
                NO_ERROR,
                0);

            ServiceStop();
            return NO_ERROR;
        }

        //
        // Update the service status
        //
        case SERVICE_CONTROL_INTERROGATE:
        {
            (VOID)UpdateServiceStatus(
                g_ServiceStatus.dwCurrentState,
                NO_ERROR,
                0);
            
            return NO_ERROR;
        }

        //
        // We do not support control code
        //
        default:
            return ERROR_CALL_NOT_IMPLEMENTED;
    }
}


#ifdef __cplusplus
extern "C"
#endif
void WINAPI
NtServiceMain(
    __in DWORD argc,
    __in_ecount_opt(argc) WCHAR **argv
    )
    
/*++

    Routine Description:

    This function sets up the nt service and calls SchedulerServiceStart(...).

    Arguments:

        DWORD argc - The argument count.

        WCHAR **argv - The arguments.

    Return value:

        None.

--*/

{
    DWORD Error = ERROR_SUCCESS;

    //
    // Create the global event
    //
    Error = CreateNtServiceStatusEvent();
    if (Error != ERROR_SUCCESS)
    {
        ERROR_REPORT(Error);
    }

    //
    // register the service_handle to handle the service control requests.
    //
    g_ServiceStatusHandle = RegisterServiceCtrlHandlerEx(
        NT_SERVICE_NAME,
        service_handle,
        NULL);

    if (g_ServiceStatusHandle == NULL)
    {
        ERROR_REPORT(GetLastError());
    }

    //
    //Since scheduler service is intended to run under LocalService
    //the serviceType is set at SERVICE_WIN32_OWN_PROCESS
    //
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;

    //
    // Report status that the service in starting to service control manager.
    //
    Error = UpdateServiceStatus(
        SERVICE_START_PENDING,
        NO_ERROR,
        DEFAULT_WAIT_HINT
        );

    if (Error != ERROR_SUCCESS)
    {
        ERROR_REPORT(Error);
    }
    
    //
    // Start the service
    //
    ServiceStart(argc, argv);
}
